package tcln.gxtx.client.utils.async;

import com.google.gwt.user.client.Command;
import com.google.gwt.user.client.DeferredCommand;
import com.google.gwt.user.client.Timer;

/**
 * A small utility class for deferred command scheduling.
 * 
 * @author Truong Chau Lien Ngoc (lienngoc@gmail.com)
 */
public final class DeferredCommandScheduler {

    /**
     * A simple callback for deferred action.
     * 
     * @author Truong Chau Lien Ngoc (lienngoc@gmail.com)
     */
    public interface DeferredActionCallback {

        /**
         * Called after a deferred command finishes.
         * 
         * @param cause
         *            Error cause thrown while invoking a deferred command. If there is no error (ie. this command is
         *            executed completely), this argument is <code>null</code>.
         */
        void actionPerformed(Throwable cause);
    }

    private DeferredCommandScheduler() {
        // to hide
    }

    /**
     * Schedules to run a given command <b>once</b>.
     * 
     * @param command
     *            Command to run. It must NOT be <code>null</code>.
     */
    public static void scheduleOnce(final Command command) {
        DeferredCommand.addCommand(command);
    }

    /**
     * Schedules to run a given command <b>once</b>.
     * 
     * @param command
     *            Command to run. It must NOT be <code>null</code>.
     * @param callback
     *            Callback to handle after this command finishes.
     */
    public static void scheduleOnce(final Command command, final DeferredActionCallback callback) {
        scheduleOnce(new Command() {
            @Override
            public void execute() {
                runCommand(command, callback);
            }
        });
    }

    /**
     * Schedules to run a given command <b>once</b>.
     * 
     * @param command
     *            Command to run. It must NOT be <code>null</code>.
     * @param delay
     *            Delay in milliseconds.
     */
    public static void scheduleOnce(final Command command, int delay) {
        scheduleOnce(command, delay, null);
    }

    /**
     * Schedules to run a given command <b>once</b>.
     * 
     * @param command
     *            Command to run. It must NOT be <code>null</code>.
     * @param delay
     *            Delay in milliseconds.
     * @param callback
     *            Callback to handle after this command finishes.
     */
    public static void scheduleOnce(final Command command, int delay, final DeferredActionCallback callback) {
        Timer timer = new Timer() {
            @Override
            public void run() {
                runCommand(command, callback);
            }
        };
        timer.schedule(delay);
    }

    /**
     * Schedules to run a given command <b>repeatedly</b>.
     * 
     * @param command
     *            Command to run. It must NOT be <code>null</code>.
     * @param interval
     *            Repetion interval in milliseconds.
     */
    public static void scheduleRepeating(final Command command, int interval) {
        Timer timer = new Timer() {
            @Override
            public void run() {
                command.execute();
            }
        };
        timer.scheduleRepeating(interval);
    }

    private static void runCommand(Command command, DeferredActionCallback callback) {
        RuntimeException cause = null;
        try {
            command.execute();
        } catch (RuntimeException e) {
            cause = e;
        }
        
        if (callback != null) {
            callback.actionPerformed(cause);
        } else if (cause != null) {
            throw cause;
        } else {
            // no error
        }
    }
}
